library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;

entity switchmanagement is
port(
	clk,reset: in std_logic;
	request: in std_logic;
	receive: in std_logic;
	D_addr, S_addr: in std_logic_vector(7 downto 0);
	inport_num: in std_logic_vector(1 downto 0);
	outport_num: out std_logic_vector(1 downto 0);
	ready: out std_logic;
	broadcast: out std_logic
);
end switchmanagement;

architecture a of switchmanagement is

component cam_table
port(
	clk,reset: in std_logic;
	D_addr: in std_logic_vector(47 downto 0);
	S_addr: in std_logic_vector(47 downto 0);
	inport_num: in std_logic_vector(1 downto 0);
	outport_num: out std_logic_vector(1 downto 0);
	complete,hit: out std_logic
);
end component;

type state_type is (idle, busy, send);
signal state, state_next: state_type;
signal D_addr_tem, S_addr_tem: std_logic_vector(47 downto 0);
signal D_addr_in, S_addr_in: std_logic_vector(47 downto 0);
signal inport_num_in,outport_num_in: std_logic_vector(1 downto 0);
signal complete,hit:std_logic;
signal broadcast_tem,ready_tem:std_logic;
signal hit_flag,hit_flag_tem:std_logic;
signal counter_next,counter:std_logic_vector(2 downto 0);
signal inport_num_tem,outport_num_tem:std_logic_vector(1 downto 0):="00";

begin 

cam: cam_table port map(clk,reset,D_addr_in,S_addr_in,inport_num_in,outport_num_in,complete,hit);

process(clk,reset)
begin
	if reset='1' then
		state<=idle;
		
	elsif(clk'event and clk='1') then
		state<=state_next;
		counter<=counter_next;
		outport_num<=outport_num_tem;
		hit_flag<=hit_flag_tem;
	end if;
end process;

process(state,request,counter)
begin
	if (state=idle) then
		if (request='1' and counter="110")then
			counter_next<="000";
		elsif (request='1')then
			counter_next<=counter+1;
		else
			counter_next<="000";
		end if;
    else
		counter_next<="000";
    end if;
end process;

process(state,complete,receive,request,counter,hit_flag)
begin
	case state is
		when idle => 
			if (request='1' and counter="110") then 
				state_next <= busy;
			else
				state_next <= state;
			end if;
		when busy =>
			if complete='1' then
				state_next <= send;
			else
				state_next <= state;
			end if;
		when send =>
			if receive='1' then
				state_next <= idle;
			else
				state_next <= state;
			end if;
		when others => 
			state_next <= idle;
		end case;
		
end process;

process(counter,clk)
begin
    if (clk'event and clk='1') then
	case counter is 
		when "001" => D_addr_tem(7 downto 0)<=D_addr;
					  S_addr_tem(7 downto 0)<=S_addr;
					  D_addr_tem(47 downto 8)<=(others=>'0');
					  S_addr_tem(47 downto 8)<=(others=>'0');
		when "010" => D_addr_tem(15 downto 8)<=D_addr;
				      S_addr_tem(15 downto 8)<=S_addr;
				      D_addr_tem(47 downto 16)<=(others=>'0');
					  S_addr_tem(47 downto 16)<=(others=>'0');
					  D_addr_tem(7 downto 0)<=D_addr_tem(7 downto 0);
					  S_addr_tem(7 downto 0)<=S_addr_tem(7 downto 0);
		when "011" => D_addr_tem(23 downto 16)<=D_addr;
				      S_addr_tem(23 downto 16)<=S_addr;
				      D_addr_tem(47 downto 24)<=(others=>'0');
					  S_addr_tem(47 downto 24)<=(others=>'0');
					  D_addr_tem(15 downto 0)<=D_addr_tem(15 downto 0);
					  S_addr_tem(15 downto 0)<=S_addr_tem(15 downto 0);
		when "100" => D_addr_tem(31 downto 24)<=D_addr;
		              S_addr_tem(31 downto 24)<=S_addr;
		              D_addr_tem(47 downto 32)<=(others=>'0');
					  S_addr_tem(47 downto 32)<=(others=>'0');
					  D_addr_tem(23 downto 0)<=D_addr_tem(23 downto 0);
					  S_addr_tem(23 downto 0)<=S_addr_tem(23 downto 0);
		when "101" => D_addr_tem(39 downto 32)<=D_addr;
		              S_addr_tem(39 downto 32)<=S_addr;
		              D_addr_tem(47 downto 40)<=(others=>'0');
					  S_addr_tem(47 downto 40)<=(others=>'0');
					  D_addr_tem(31 downto 0)<=D_addr_tem(31 downto 0);
					  S_addr_tem(31 downto 0)<=S_addr_tem(31 downto 0); 
		when "110" => D_addr_tem(47 downto 40)<=D_addr;
		              S_addr_tem(47 downto 40)<=S_addr;
		              D_addr_tem(39 downto 0)<=D_addr_tem(39 downto 0);
					  S_addr_tem(39 downto 0)<=S_addr_tem(39 downto 0);
		when others => D_addr_tem <= (others=>'0');
		               S_addr_tem <= (others=>'0');
	end case;
	end if;
end process;

process(state,D_addr_tem,S_addr_tem,inport_num_tem)
begin
	if (state=busy ) then
		D_addr_in<=D_addr_tem;
		S_addr_in<=S_addr_tem;
		inport_num_in<=inport_num_tem;
	else
		D_addr_in<=(others=>'0');
		S_addr_in<=(others=>'0');
		inport_num_in<=(others=>'0');
	end if;
end process;

process(state,hit,hit_flag)
begin
	if (state=busy and hit='1')then
		hit_flag_tem<='1';
	elsif (state=idle)then
		hit_flag_tem<='0';
	else
	    hit_flag_tem<=hit_flag;
	end if;
end process;

process(state,outport_num_in,inport_num_in,ready_tem,inport_num_tem,hit_flag)
begin
	if state=send then
		if hit_flag='1' then
			outport_num_tem<=outport_num_in;
			broadcast_tem<='0';
		elsif ready_tem='1' then
			outport_num_tem<=inport_num_tem;
			broadcast_tem<='1';
		else
			outport_num_tem<="00";
			broadcast_tem<='0';
		end if;
		ready_tem<='1';
	else
		outport_num_tem<=(others=>'0');
		broadcast_tem<='0';
		ready_tem<='0';
	end if;
end process;

process(clk)
begin
	if (clk'event and clk='1')then
		if request = '1' then
			if inport_num/="00" then
				inport_num_tem<=inport_num;
			end if;
		else
			inport_num_tem<=(others=>'0');
		end if;
	end if;
end process;

ready<=ready_tem;
broadcast<=broadcast_tem;
end a;